#include "type/type.hpp"
#include "parser/lexer.hpp"
#include "runtime/universe.hpp"
#include "runtime/heap.hpp"

#include <cstdio>

void* TypeMeta::operator new(size_t size) {
    return Universe::type_space->allocate(size);
}

bool IntType::support_operator(OperatorType ot) {
    if (ot == OPERATOR_ADD || ot == OPERATOR_SUB ||
            ot == OPERATOR_MUL || ot == OPERATOR_DIV ||
            ot == OPERATOR_SHIFT) {
        return true;
    }

    return false;
}

bool CharType::support_operator(OperatorType ot) {
    if (ot == OPERATOR_ADD || ot == OPERATOR_SUB) {
        return true;
    }

    return false;
}

bool StringType::support_operator(OperatorType ot) {
    if (ot == OPERATOR_ADD || ot == OPERATOR_MUL) {
        return true;
    }

    return false;
}

ArrowType::ArrowType(Type* s, Type* d) {
    _type_code = TYPE_ARROW;
    _src = s;
    _dst = d;
    _name = (char*)Universe::type_space->allocate(sizeof(char) * LEN);
    if (_src->is_primitive()) {
        snprintf(_name, LEN, "%s -> %s", _src->to_string(), _dst->to_string());
    }
    else {
        snprintf(_name, LEN, "(%s) -> %s", _src->to_string(), _dst->to_string());
    }
}

ArrowType::~ArrowType() {
    if (_name != NULL) {
        delete[] _name;
        _name = NULL;
    }
}

const char* ArrowType::to_string() {
    return _name;
}

bool ArrowType::equals(Type* t) {
    ArrowType* at = dynamic_cast<ArrowType*>(t);
    if (at == NULL) {
        return false;
    }

    return _src->equals(at->src()) && _dst->equals(at->dst());
}

UserDefType::UserDefType(Token* t) {
    _type_code = TYPE_CONSTRUCTOR;
    _name = (char*)Universe::type_space->allocate(t->_length + 1);
    _name[t->_length] = '\0';
    strncpy(_name, t->_value, t->_length);
}

UserDefType::~UserDefType() {
    delete[] _name;
}

Type* TypeFunction::apply(TypeArgs* real_args) {
    return NULL;
}

TypeVar::TypeVar(Token* t) {
    _type_code = TYPE_VAR;
    _name = (char*)Universe::type_space->allocate(t->_length + 1);
    _name[t->_length] = '\0';
    strncpy(_name, t->_value, t->_length);
}

void TypeArgs::init_length(int n) {
    _length = n;
    _args = (Type**)Universe::type_space->allocate(sizeof(Type*) * n);
}

void TypeArgs::set_arg(int i, Type* tv) {
    _args[i] = tv;
}

